package com.yuanxuzhen.androidmedia.encode;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.yuanxuzhen.androidmedia.IHanlderCallback;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class YuvEncoder {
    private int mWidth;
    private int mHeight;
    private String mDstFilePath = null;
    private String mSrcFilePath = null;
    MediaCodec mEncorder;
    ExecutorService mExecutorService;
    private int mBufferSize;
    private IHanlderCallback mCallback;
    private boolean isEncodeing = false;


    public void init(Context context, int width,
                     int height,
                     String srcPath, String dstPath,
                     IHanlderCallback callback) {
        mWidth = width;
        mHeight = height;
        mSrcFilePath = srcPath;
        mDstFilePath = dstPath;
        mExecutorService = Executors.newCachedThreadPool();
        mCallback = callback;
        mBufferSize =mWidth * mHeight * 3 / 2;
        Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
        try {
            mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

        } catch (Exception e) {
            e.printStackTrace();
        }
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
                mWidth,
                mHeight);
        format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileMain);
        format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * 1000);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, 25);//帧率，一般在15至30之内，太小容易造成视频卡顿。
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);  //250帧一个关键帧
        format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 3);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);//色彩格式，具体查看相关API，不同设备支持的色彩格式不尽相同
        mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    }
    public void startEncording() {
        mExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                startEncordingV1();
            }
        });
    }

    /**
     * 开始编码
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void startEncordingV1() {
        if (mEncorder == null) {
            isEncodeing = false;
            if (mCallback != null) {
                mCallback.onFail();
            }
            return;
        }
        isEncodeing = true;
        mEncorder.start();
        try {
            FileInputStream inputStream = new FileInputStream(mSrcFilePath);
            FileOutputStream mFileStream = new FileOutputStream(mDstFilePath);
            MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
            while (true) {
                long a = System.currentTimeMillis();
                // 从队列中取出录音的一帧音频数据
                byte[] byteArray = new byte[mBufferSize];
                int readSize = inputStream.read(byteArray);
                if (readSize <= 0) {
                    break;
                }
                ByteBuffer buf = ByteBuffer.wrap(byteArray);
                // 取出InputBuffer，填充音频数据，然后输送到编码器进行编码
                int inputBufferIndex = mEncorder.dequeueInputBuffer(0);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
                    inputBuffer.clear();
                    inputBuffer.put(buf);
                    mEncorder.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime(), 0);
                }

                // 取出编码好的一帧音频数据，然后给这一帧添加ADTS头
                int outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
                    int outBufferSize = outputBuffer.limit();
                    byte[] aacBytes = new byte[outBufferSize];
                    outputBuffer.get(aacBytes, 0, outputBuffer.limit());
                    mFileStream.write(aacBytes);
                    mEncorder.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                }

                long b = System.currentTimeMillis() - a;
                Log.i("AudioEncode", "编码耗时-毫秒==" + b);
            }
            if (mFileStream != null) {
                try {
                    mFileStream.flush();
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (inputStream != null) {
                try {
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (mEncorder != null) {
                mEncorder.stop();
            }
            if (mCallback != null) {
                mCallback.onSuccess();
            }
        } catch (FileNotFoundException e) {
            if (mCallback != null) {
                mCallback.onFail();
            }
            e.printStackTrace();
        } catch (IOException e) {
            if (mCallback != null) {
                mCallback.onFail();
            }
            e.printStackTrace();
        } finally {
            isEncodeing = false;
        }
    }



    public void release() {
        if (mEncorder == null) {
            return;
        }
        mEncorder.release();
        mEncorder = null;
        isEncodeing = false;
    }

    public boolean isEncodeing() {
        return isEncodeing;
    }
}
